import fs from 'fs'
import path from 'path'

const projectPath = process.cwd()
const packagePath = path.join(projectPath, 'package.json')
const packageJson = JSON.parse(await fs.readFileSync(packagePath, 'utf-8'))

export const module_template = `
import { renderToString } from 'react-dom/server'
import { createRoot } from 'react-dom/client'
import App from '#/<!--app-component-path-->'

if(typeof document != 'undefined'){
  createRoot(document.getElementById('root')).render(<App/>)
}
export function render() {
  return renderToString(<App/>)
}
`

export const html_develop_template = `
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>${packageJson.title}</title>
  </head>
  <body style="margin:0; padding:0;">
    <div id="root"><!--app-html--></div>
    <script type="module" src="<!--app-client-runtime-->"></script>
  </body>
</html>
`

export const build = async(path) => {
  const { build } = await import('vite')
  const { renderToString } = await import("react-dom/server")
  const react = await import('@vitejs/plugin-react-swc').then(m=>m.default)
  const cssInjectedByJsPlugin = await import('vite-plugin-css-injected-by-js').then(m=>m.default)

  !fs.existsSync('.runtime') && fs.mkdirSync('.runtime')
  !fs.existsSync('.runtime/server/') && fs.mkdirSync('.runtime/server/')
  !fs.existsSync('.runtime/client/') && fs.mkdirSync('.runtime/client/')

  const { name, jsxPath, htmlPath, serverPath, clientPath } = getModulePath(path)
  // create jsx path file in dev mode
  fs.writeFileSync(jsxPath, module_template.trim().replace('<!--app-component-path-->', path))

  // create client js
  await build({
    plugins: [react(),cssInjectedByJsPlugin()], 
    build: { 
      minify: true, 
      emptyOutDir: false,
      rollupOptions: { 
        input: jsxPath,
        output: {
          dir: '.runtime/client/',
          entryFileNames: name + '.js'
        }
      } 
    } 
  })

  // create server js
  await build({
    plugins: [react()], 
    build: { 
      ssr: true, 
      emptyOutDir: false, 
      rollupOptions: { 
        input: path, 
        output: { 
          dir: '.runtime/server/', 
          entryFileNames: name + '.js' 
        } 
      } 
    }
  })
  
  const Page = await import('../../' + serverPath).then(m => m.default)
  const html = renderToString(Page)
  fs.writeFileSync(htmlPath, html_develop_template.replace('<!--app-html-->', html).replace('<!--app-client-runtime-->', '/' + clientPath.substring(16)))
  fs.unlinkSync(serverPath)
  fs.unlinkSync(jsxPath)
}

export const getModulePath = (path) => {
  const name = Buffer.from(path, 'utf-8').toString('base64')
  const jsxPath = '.runtime/' + name + '.jsx'
  const htmlPath = '.runtime/server/' + name + '.html'
  const serverPath = '.runtime/server/' + name + '.js'
  const clientPath = '.runtime/client/' + name + '.js'
  return { name, jsxPath, htmlPath, serverPath, clientPath }
}

export const getDevRender = async(ctx, page) => {
  const { jsxPath } = getModulePath(page)
  if(!fs.existsSync(jsxPath)){
    !fs.existsSync('.runtime') && fs.mkdirSync('.runtime')
    fs.writeFileSync(jsxPath, module_template.trim().replace('<!--app-component-path-->', page))
  }
  const vite = ctx.app.vite
  const template = await vite.transformIndexHtml(ctx.path, html_develop_template)
  const { render } = await vite.ssrLoadModule(jsxPath)
  return template.replace('<!--app-html-->', await render()).replace('<!--app-client-runtime-->', '/' + jsxPath)
}

export const getHtml = (page) => {
  const { htmlPath } = getModulePath(page)
  return fs.readFileSync(htmlPath, 'utf-8')
}